// CrySoundSystem.cpp : Defines the entry point for the DLL application.
//

#include "StdAfx.h"
#include "platform_impl.h"

#include <IEngineModule.h>
#include <CryExtension/ICryFactory.h>
#include <CryExtension/Impl/ClassWeaver.h>

#include "BucketAllocatorImpl.h"

#include "Sound.h"
#include "SoundSystem.h"
#include "IAudioDevice.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400
	#include "AudioDeviceFmodEx400.h"
#else
	#include "AudioDeviceFmodEx400_NULL.h"
#endif





CSoundAllocator::CSoundAllocator()
	: m_mspaceBase(NULL)
	, m_mspaceSize(0)
	, m_mspace(NULL)
	, m_mspaceUsed(0)
	, nSmallAllocations(0)
	, nAllocations(0)
	, nSmallAllocationsSize(0)
	, nAllocationsSize(0)
{
#if CAPTURE_REPLAY_LOG && defined(USE_GLOBAL_BUCKET_ALLOCATOR)
	m_smallAllocator.ReplayRegisterAddressRange("Sound Buckets");
#endif
}

CSoundAllocator::~CSoundAllocator()
{
	if (m_mspace)
		m_mspace->Release();
}

void* CSoundAllocator::AllocateRaw(size_t nSize, size_t nAlign, const char *sUsage)
{
	// Redirect small allocattions through a bucket allocator
	if( nSize <= nMaxSmallSize )
	{			
		void* ret = m_smallAllocator.allocate(nSize);
		TrackSmallAlloc(ret, m_smallAllocator.getSizeEx(ret));			
		return ret;
	}

	FRAME_PROFILER("CSoundAllocator::Allocate", GetISystem(), PROFILE_SOUND);

	void* ret = m_mspace->Memalign(nAlign, nSize, sUsage);
	if (ret)
		CryInterlockedAdd(&m_mspaceUsed, m_mspace->UsableSize(ret));

	TrackGeneralAlloc(ret, nSize);		

	return ret;
}

bool CSoundAllocator::Free( void *ptr )
{
	if (ptr && m_smallAllocator.IsInAddressRange(ptr))
	{
		size_t sz = m_smallAllocator.deallocate(ptr);
		TrackSmallDealloc(ptr);

		return true;
	}
	else
	{
		size_t sz = m_mspace->Free(ptr);
		if (sz)
		{
			TrackGeneralDealloc(sz);
			CryInterlockedAdd(&m_mspaceUsed, -static_cast<int>(sz));

			return true;
		}
	}

	return false;
}

size_t CSoundAllocator::Size( void *ptr )
{			
	if (m_smallAllocator.IsInAddressRange(ptr))
	{
		return m_smallAllocator.getSizeEx(ptr);
	}
	else
	{
		return m_mspace->UsableSize(ptr);
	}
}

void CSoundAllocator::InitMem(size_t nSize, void* ptr)
{
	m_mspace = CryGetIMemoryManager()->CreateGeneralMemoryHeap(ptr, nSize,"SoundPool");
	m_mspaceBase = ptr;
	m_mspaceSize = nSize;
}

void CSoundAllocator::UnInitMem()
{
	if (m_mspace)
		m_mspace->Release();

	m_mspaceBase = NULL;
	m_mspaceSize = 0;
	m_mspace = NULL;
	m_mspaceUsed = 0;
	nSmallAllocations = 0;
	nAllocations = 0;
	nSmallAllocationsSize = 0;
	nAllocationsSize = 0;
}

void CSoundAllocator::Cleanup()
{
#if defined(USE_GLOBAL_BUCKET_ALLOCATOR)
	m_smallAllocator.cleanup();
#endif
}

//////////////////////////////////////////////////////////////////////////
struct CSystemEventListner_Sound : public ISystemEventListener
{
public:
	virtual void OnSystemEvent( ESystemEvent event,UINT_PTR wparam,UINT_PTR lparam )
	{
		switch (event)
		{
		case ESYSTEM_EVENT_LEVEL_LOAD_START:
			{
				g_MemoryPoolPrimary.Cleanup();
				break;
			}
		case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
			{
				STLALLOCATOR_CLEANUP;
				g_MemoryPoolPrimary.Cleanup();
				break;
			}
		case ESYSTEM_EVENT_RANDOM_SEED:
			{
				g_random_generator.seed(gEnv->bNoRandomSeed?0:(uint32)wparam);
				break;
			}
		}
	}
};

static CSystemEventListner_Sound g_system_event_listener_sound;

//////////////////////////////////////////////////////////////////////////////////////////////
// dll interface	

  
void AddPhysicalBlock(long size)
{
#ifndef _LIB
	_CryMemoryManagerPoolHelper::FakeAllocation( size );
#else
	GetISystem()->GetIMemoryManager()->FakeAllocation( size );
#endif
}


//////////////////////////////////////////////////////////////////////////
class CEngineModule_CrySoundSystem : public IEngineModule
{
	CRYINTERFACE_SIMPLE(IEngineModule)
	CRYGENERATE_SINGLETONCLASS(CEngineModule_CrySoundSystem, "EngineModule_CrySoundSystem", 0xec73cf4362ca4a7f, 0x8b451076dc6fdb8b)

	//////////////////////////////////////////////////////////////////////////
	virtual const char *GetName() { return "CrySoundSystem"; };
	virtual const char *GetCategory() { return "CryEngine"; };

	//////////////////////////////////////////////////////////////////////////
	virtual bool Initialize( SSystemGlobalEnvironment &env,const SSystemInitParams &initParams )
	{
		ISystem* pSystem = env.pSystem;

		void *hWnd = initParams.hWnd;

		CSoundSystem* pSoundSystem = NULL;
		IAudioDevice *pAudioDevice = NULL;
		bool bInitDeviceResult = false;
		bool bSoundSystemResult = false;


#ifdef SOUNDSYSTEM_USE_FMODEX400
		// creating a FMOD AudioDevice for a Win32/64 platform and hook it into the SoundSystem
		pAudioDevice	= new CAudioDeviceFmodEx400(hWnd);
#endif






		if (pAudioDevice)
		{
			// We have created the audio device now lets create sound system
			pSoundSystem = new CSoundSystem(hWnd, pAudioDevice);

			if (pSoundSystem)
			{
				env.pSoundSystem = static_cast<ISoundSystem*>(pSoundSystem);

				// time to init device if everything ok with soundsystem
				if (pSoundSystem->IsOK())
				{
					CryLogAlways("<Sound> Initializing AudioDevice now!\n");
					bInitDeviceResult = pAudioDevice->InitDevice(pSoundSystem);
				}

				if (bInitDeviceResult)
				{
					CryLogAlways("<Sound> Initializing SoundSystem now!\n");
					bSoundSystemResult = pSoundSystem->Init();
				}
				else
					CryLogAlways("<Sound> Error: Cannot initialize SoundSystem!\n");
			}
			else
				CryLogAlways("<Sound> Error: Cannot create SoundSystem!\n");
		}

		if (!pSoundSystem || !bSoundSystemResult || !pAudioDevice || !bInitDeviceResult)
		{
			//if the sound system cannot be created or initialized,
			// or the console variable s_DummySound = 1 then
			//create the dummy sound system (NULL sound system, same as for
			//dedicated server)

			// Cleanup what was created
			if (pSoundSystem)
			{
				pSoundSystem->Release();
				pSoundSystem = NULL;
				pAudioDevice = NULL;
			}

			if (pAudioDevice)
			{
				pAudioDevice->ShutDownDevice();
				delete pAudioDevice;
				pAudioDevice = NULL;
			}
		}

		pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_sound);

		env.pSoundSystem = pSoundSystem;
	
		return true;
	}
};

CRYREGISTER_CLASS(CEngineModule_CrySoundSystem)

CEngineModule_CrySoundSystem::CEngineModule_CrySoundSystem()
{
};

CEngineModule_CrySoundSystem::~CEngineModule_CrySoundSystem()
{
};

#include <CrtDebugStats.h>
